package engine;

import java.util.ArrayList;
import java.util.List;

import com.google.gson.annotations.Expose;

public class Player{
	static final int FOLD = 0;
	static final int CHECK = 1;
	static final int CALL = 2;
	static final int RAISE = 3;
	static final int ALLIN = 4;
	static final int ALLINRAISE = 5; // mängija läheb ALLIN, ning seeläbi raiseb
	@Expose 
	int id;
	@Expose
	String name;
	@Expose
	int money;
	@Expose
	int bet;
	@Expose
	int action;
	boolean isAllIn;
	@Expose
	boolean isHuman;
	@Expose
	Hand hand;
	Game game;
	
	public Player(int id, String name, int money, Game game){
		this.id = id;
		this.name = name;
		this.money = money;	
		this.hand = new Hand();
		this.bet = 0; //mängija kogusumma pot'is
		this.isHuman = false;
		this.action = -1; // vastavalt mängija otsusele, omandab see muutuja kas FOLD, CHECK, CALL või RAISE väärtuse
		this.isAllIn = false;
		this.game = game;
	}

	public void fold(){
		//eemaldab mängija kes on foldinud

		this.bet = 0;
		System.out.println("FOLD " + this.name);
		this.action = FOLD;
		synchronized(game.messenger){
			game.messenger.callAction(this, "updateActivePlayer(player.id); updatePlayerDetails(player); hidePlayerCards(player.id); updatePot("+game.pot+")");
		}
		this.endTurn(FOLD);
	}


	public void checkCall(){
		if (game.max(game.playersBets) - game.playersBets[this.id] > 0) { 
			// kui keegi on round'is panustanud rohkem, kui sina, siis on sul võimalik callida
			int toCall;
			if (game.max(game.playersBets) - game.playersBets[this.id] >= this.money){
				toCall = this.money;
				this.money = 0;
				this.goAllIn(game.playersBets[this.id] + toCall);
				this.action = ALLIN;
			}else {
				toCall = game.max(game.playersBets) - game.playersBets[this.id];
				this.money -= game.max(game.playersBets) - game.playersBets[this.id];
				System.out.println("CALL " + this.name + ", $" + (toCall));
				this.action = CALL;
			}
			game.pot += toCall;
			game.playersBets[this.id] += (toCall);
			this.bet += game.playersBets[this.id];
			
		}
		else {
			System.out.println("CHECK " + this.name);
			this.action = CHECK;
		}
		synchronized(game.messenger){
			game.messenger.callAction(this, game.playersBets[this.id], "updateActivePlayer(player.id); addPlayerBet(player,bet); updatePlayerDetails(player); updatePot("+game.pot+")");
		}
		if (!this.isAllIn){
			this.endTurn(CALL);
		} else {
			this.endTurn(ALLIN);
		}
	}
	//
	public void raise(){
		if (game.betCounter < game.maxBets){
			int raise = (game.maxBetAmount + game.max(game.playersBets)) - game.playersBets[this.id];
			if (raise >= this.money) {
				// kui järgmine raise on suurem või võrdne, kui mängijal raha on, siis mängija ALLIN
				if (raise == this.money){
					game.betCounter += 1;
				}
				raise = this.money;
				this.money = 0;
				game.pot += raise;
				int prevBetsMax = game.max(game.playersBets);
				game.playersBets[this.id] += raise;
				this.goAllIn(game.playersBets[this.id]);
				this.bet += game.playersBets[this.id];
				this.action = ALLIN;
				synchronized(game.messenger){
					game.messenger.callAction(this, game.playersBets[this.id], "updateActivePlayer(player.id); addPlayerBet(player,bet); updatePlayerDetails(player); updatePot("+game.pot+")");
				}
				if (game.playersBets[this.id] > prevBetsMax){ 
					//kui raise on eelmistest panustest suurem
					this.endTurn(ALLINRAISE);
				}
				else {
					this.endTurn(ALLIN);
				}
			}
			else {
				this.money -= raise;
				game.betCounter += 1;
				game.playersBets[this.id] += raise;
				System.out.println("RAISE " + this.name + ", to $" + game.playersBets[this.id]);
				game.pot += raise;

				this.bet += game.playersBets[this.id];
				this.action = RAISE;
				synchronized (game.messenger){
					game.messenger.callAction(this, game.playersBets[this.id], "updateActivePlayer(player.id); addPlayerBet(player,bet); updatePlayerDetails(player); updatePot("+game.pot+")");
				}
				this.endTurn(RAISE);
			}
		}
		else {
			this.checkCall();
		}
	}

	public void goAllIn(int amount){
		System.out.println("ALL IN " + this.name + ", $" + (amount));
		this.isAllIn = true;
		game.allInPlayers.add(this);
	}

	public String getPlayerHand(){
		return this.hand.cards.get(0).getCardName() + ", " + this.hand.cards.get(1).getCardName();
	}

	public void endTurn(int action){	
		if (game.currentPlayers.indexOf(this)+1 < game.currentPlayers.size()){
			// võtame järgmise mängija aktiivseks mängijaks
			game.activePlayer = game.currentPlayers.get(game.currentPlayers.indexOf(this)+1);
		}else {
			if (game.currentPlayers.size() > 0){
				game.activePlayer = game.currentPlayers.get(0);
			}
		}
		if (action == RAISE){
			// tõstame järjekorra ümber, viimane, kes räägib, on tõstjale eelnev mängija
			game.currentPlayers = splice(game.currentPlayers, game.currentPlayers.indexOf(this)+1);
			game.lastPlayerToAct = game.currentPlayers.get(game.currentPlayers.size()-2);
			if (this == game.lastPlayerToAct){
				// kui oleme järjendi lõpus, siis järgmine mängija on järjendi esimene mängija
				game.activePlayer = game.currentPlayers.get(0);
				System.out.println(game.activePlayer.name);
			}
		}

		if (action == FOLD || action == ALLIN){
			// eemaldame mängija antud käe mängijate järjendist
			game.currentPlayers.remove(this);
			game.currentPlayersUnordered.remove(this);
		}

		if (action == ALLINRAISE){ // kui mängija raiseb ning läheb sellega ALLIN
			game.currentPlayers = splice(game.currentPlayers, game.currentPlayers.indexOf(this)+1);
			game.lastPlayerToAct = game.currentPlayers.get(game.currentPlayers.size()-2);
			game.activePlayer = game.currentPlayers.get(0);
			//tõstame järjekorra ümber ja eemaldame ALLIN mängija järjekorrast
			game.currentPlayers.remove(this);
			game.currentPlayersUnordered.remove(this);
		}

		if (game.currentPlayers.size() == 1 && (game.allInPlayers.size() == 0)){
			//kui jäänud on üks panustaja (panustaja võib olla ALLIN)
			List<Player> tmp = new ArrayList<Player>();
			tmp.add(game.currentPlayers.get(0));
			game.winnerIs(tmp);
		}
		else if ((game.currentPlayers.size() == 0 && (game.allInPlayers.size() == 1))){
			List<Player> tmp = new ArrayList<Player>();
			tmp.add(game.allInPlayers.get(0));
			game.winnerIs(tmp);
		}
		else if ((this == game.lastPlayerToAct && (action == CALL || action == FOLD || action == ALLIN)) || (game.currentPlayers.size() == 0  && game.allInPlayers.size() > 1)){ 
			//kui kord on viimase mängija käes, ning ta CALLib või FOLDib, siis jätkame mänguga
			if (game.currentPlayers.size() < 2 && game.allInPlayers.size() >= 1){
				while (game.NEXTPHASE < game.SHOWDOWN){ //mäng showdownini välja
					game.start(game.NEXTPHASE);
				}
				try {
					Thread.sleep(2000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				game.start(game.NEXTPHASE);
			}else {
				game.start(game.NEXTPHASE);
			}

		}
		if (game.currentPlayers.size() > 0){
			if (!game.activePlayer.isHuman && game.NEXTPHASE != game.PREFLOPGAME){
				try {
					Thread.sleep(500);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				game.ai.getAction(game.activePlayer);
				
			} else if (game.NEXTPHASE != game.PREFLOPGAME){
				synchronized(game.messenger){
					game.messenger.callAction(game.human, "updateActivePlayer(player.id)");
				}
				game.human.action = -1;
				//game.messenger.callAction("stopUpdate()");
			}
		}
		game.updateButtons();
	}
	
	public static List<Player> copy(List<Player> a){
		List<Player> b = new ArrayList<Player>();
		for (int i = 0; i < a.size(); i++) {
			b.add(a.get(i));
		}
		return b;
	}
	
	public static List <Player> splice(List<Player> a, int i){
		List <Player> tmp = new ArrayList <Player>();
		tmp = copy(a.subList(i, a.size()));
		tmp.addAll(a.subList(0,i));
		return tmp;
	}
}